#include "Socket.h"
#include <iostream>

#define CONNECTION_TIME_OUT 2

Socket::Socket() {
	this->fd = ::socket(AF_INET, SOCK_STREAM, 0);
	this->_valido = true;

	// PRUEBA TIMEOUT

			timeval timeoutval;
			timeoutval.tv_sec = CONNECTION_TIME_OUT;
			timeoutval.tv_usec = 0;

//			if (setsockopt(this->fd, SOL_SOCKET, SO_RCVTIMEO, &timeoutval, sizeof(timeval)) == -1) {
//				perror("setsockoptRC");
//			}
//
			if (setsockopt(this->fd, SOL_SOCKET, SO_SNDTIMEO, &timeoutval, sizeof(timeval)) == -1) {
				perror("setsockoptSD");
			}

	//PRUEBA TIMEOUT
}

Socket::Socket(int fd){
	this->_valido = true;
	this->fd = fd;

	// PRUEBA TIMEOUT

			timeval timeoutval;
			timeoutval.tv_sec = CONNECTION_TIME_OUT;
			timeoutval.tv_usec = 0;

//			if (setsockopt(this->fd, SOL_SOCKET, SO_RCVTIMEO, &timeoutval, sizeof(timeval)) == -1) {
//				perror("setsockoptRC");
//			}

			if (setsockopt(this->fd, SOL_SOCKET, SO_SNDTIMEO, &timeoutval, sizeof(timeval)) == -1) {
				perror("setsockoptSD");
			}

	//PRUEBA TIMEOUT
}


int Socket::connect(const std::string& host, unsigned int port){

	if (!this->_valido){
		this->fd = ::socket(AF_INET, SOCK_STREAM, 0);
		this->_valido = true;
	}

	struct hostent* he = gethostbyname(host.c_str());
	if (he == NULL){
		return SOCKET_ERROR;
	}

	this->direccion.sin_family = AF_INET;
	this->direccion.sin_port = htons(port);
	this->direccion.sin_addr = *((struct in_addr *) he->h_addr);

	int retorno = ::connect(this->fd, (sockaddr*)&direccion, sizeof(struct sockaddr));
	perror("connect");

	return retorno;
}

int Socket::listen(unsigned int port, unsigned int nro_cliente){

	if (!this->_valido){
		this->fd = ::socket(AF_INET, SOCK_STREAM, 0);
		this->_valido = true;
	}

	this->direccion.sin_family = AF_INET;
	this->direccion.sin_port = htons(port);
	this->direccion.sin_addr.s_addr = INADDR_ANY;

	int yes = 1;
	if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
		perror("setsockopt");
	}

	if(bind(this->fd, (struct sockaddr* )&direccion, sizeof this->direccion) < 0){
		return SOCKET_ERROR;
	}

	return ::listen(this->fd, nro_cliente);
}

int Socket::checkPortAvailability(int port){
	int retorno = SOCKET_EXITO;

	int fd = ::socket(AF_INET, SOCK_STREAM, 0);

	if(fd < 0){
		return SOCKET_ERROR;
	}

	sockaddr_in direccion;
	direccion.sin_family = AF_INET;
	direccion.sin_port = htons(port);
	direccion.sin_addr.s_addr = INADDR_ANY;


	if(bind(fd, (struct sockaddr* )&direccion, sizeof direccion) < 0){
		retorno = SOCKET_ERROR;
	}

	::close(fd);

	return retorno;
}

int Socket::close(){
	this->_valido = false;
	return (::close(this->fd));
}





int Socket::accept(){
	struct sockaddr_in direc;
	unsigned int tamanio = (socklen_t) sizeof(direc);
	int retorno = ::accept(this->fd, (sockaddr*)&direc, &tamanio);
	perror("accept");
	return retorno;
}

bool Socket::esValido(){
	return (this->_valido);
}

int Socket::shutdown(){

	if(this->_valido){
		int retorno = ::shutdown(this->fd, SHUT_RDWR);

		this->_valido = false;
		if (retorno==-1) {
			perror("shutdown");
		}

		return retorno;
	}
	else
		return SOCKET_ERROR;
}

Socket::~Socket() {

}
